#include "crossbit/Exit.h"
#include "arch/x86/X86TraceManager.h"

using namespace crossbit;

X86TraceManager::X86TraceManager( MemoryManager *mm ) : TraceManager(mm)
{
				/***********************************************
				 * Machine code: 8d8a00000000 e305 e900000000 
				 * e900000000 90909090
				 * asm: lea (%edx), %ecx
				 *      jecxz l1
				 *      jmp 00000000
				 * l1:  jmp 00000000
				 *
				 *********************************************/
				*((XTInt32*)&sample[0]) = 0x00008a8d;
				*((XTInt32*)&sample[4]) = 0x05e30000;
				*((XTInt32*)&sample[8]) = 0x000000e9;
				*((XTInt32*)&sample[12]) = 0x0000e900;
				*((XTInt32*)&sample[16]) = 0x90900000;
}

X86TraceManager::~X86TraceManager()
{
				//empty now
}
void X86TraceManager::linkSuperBlock( TBlock* former_tblock , TBlock* super_tblock ){
			   XTUint8* addr = (XTUint8 *)former_tblock -> enterAddr();
		       *addr  = 0xe9;  //jmp disp
				addr++;
			   *((XTInt32 *)addr) = super_tblock -> enterAddr() - former_tblock ->enterAddr() - 5;
}
void X86TraceManager::directLinkTo( TBlock* preBlock, XTInt32 exit, TBlock* block)
{
				Exit& e = preBlock->getExit(exit);
				e.dest = block->enterAddr();

				if( e.linked == 0 )
				{
								e.linked = 1;

								XTInt8* p = e.linkCode;

								//jmp near addr
								//0xff 0x25 addr
								*p++ = 0xff;
								*p++ = 0x25;
								*(XTInt32*)p = (XTInt32)(&e.dest);
				}
}

void X86TraceManager::indirectLinkTo( TBlock* preBlock, XTInt32 exit, TBlock* block )
{
				XTMemAddr p = allocatePredictionCodeBlock();

				Exit& e = preBlock->getExit(exit);

				XTMemAddr SPC = block->getSPC();
				XTMemAddr enter = block->enterAddr();

				if( p != (XTMemAddr)NULL )
				{
								*((XTInt32*)p) = 0x00008a8d;		
								*((XTInt32*)(p+4)) = 0x05e30000;
								*((XTInt32*)(p+8)) = 0x000000e9;
								*((XTInt32*)(p+12)) = 0x0000e900;
								*((XTInt32*)(p+16)) = 0x90900000;

								*((XTInt32*)(p+2)) = 0 - SPC;

								XTInt8* pStart = e.linkCode;

								XTMemAddr tmp = (XTMemAddr) (pStart + 5);

								*pStart = 0xe9;
								*((XTInt32*)(pStart+1)) = p - tmp;

								if( e.linked == 0 )
								{
												*((XTInt32*)(p+8)) = 0xc3;  //RET
								}
								else
								{
												tmp = (XTInt32)(p+13);
												*((XTInt32*)(p+9)) = e.dest - tmp;
								}

								e.dest = p;

								tmp = p + 18;
								*((XTInt32*)(p+14)) = enter - tmp;

								e.linked = 1;
				}
}

void X86TraceManager::fillPredictionCodeBlock( TBlock* block )
{
				//empty now
}

XTMemAddr X86TraceManager::allocatePredictionCodeBlock()
{
				XTMemAddr buf;

				if( PREDICTIONCODEBLOCKLEN <= tm_size )
				{ 
						buf = tm_pool;
						tm_pool += PREDICTIONCODEBLOCKLEN;
						tm_size -= PREDICTIONCODEBLOCKLEN;
				}
				else
				{
								//it's temporarily now
								//allocate new pool here
								std::cerr << "Memory is not enough" << std::endl;
								exit(0);
				}

				return buf;
}
